﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.ServiceLocation;
using Spring.Context.Support;
using Spring.Objects.Factory;
using Spring.Objects.Factory.Config;
using Spring.Objects.Factory.Support;

namespace MetaContainer.Spring
{
   /// <summary>
   /// Adapter for MetaContainer using Spring.NET.
   /// </summary>
   public sealed class SpringMetaContainerAdapter : ServiceLocatorImplBase, IMetaContainer
   {      
      private readonly GenericApplicationContext _context;
      private readonly DefaultObjectDefinitionFactory factory = new DefaultObjectDefinitionFactory();

      /// <summary>
      /// Creates a new adapter instance, using the specified <paramref name="context"/>
      /// for resolving service instance requests.
      /// </summary>
      /// <param name="context">the actual context used for resolving service instance requests.</param>
      public SpringMetaContainerAdapter(GenericApplicationContext context)
      {
         if (context == null)
         {
            throw new ArgumentNullException("context", "A factory is mandatory");
         }
         _context = context;
      }

      /// <summary>
      /// Resolves a requested service instance.
      /// </summary>
      /// <param name="serviceType">Type of instance requested.</param>
      /// <param name="key">Name of registered service you want. May be null.</param>
      /// <returns>
      /// The requested service instance or null, if <paramref name="key"/> is not found.
      /// </returns>
      protected override object DoGetInstance(Type serviceType, string key)
      {
         if (key == null)
         {
            IEnumerator it = DoGetAllInstances(serviceType).GetEnumerator();
            if (it.MoveNext())
            {
               return it.Current;
            }
            throw new ObjectCreationException(string.Format("no services of type '{0}' defined", serviceType.FullName));
         }
         return _context.GetObject(key, serviceType);
      }

      /// <summary>
      /// Resolves service instances by type.
      /// </summary>
      /// <param name="serviceType">Type of service requested.</param>
      /// <returns>
      /// Sequence of service instance objects matching the <paramref name="serviceType"/>.
      /// </returns>
      protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
      {
         foreach (object o in _context.GetObjectsOfType(serviceType).Values)
         {
            yield return o;
         }
      }

      public void Register(Type from, Type to, string name, Scope scope)
      {
         name = name ?? Guid.NewGuid().ToString();
         ObjectDefinitionBuilder builder = ObjectDefinitionBuilder.RootObjectDefinition(factory, to)
            .SetAutowireMode(AutoWiringMode.AutoDetect)
            .SetSingleton(scope == Scope.Singleton);
         
         IObjectDefinition def = builder.ObjectDefinition;
         _context.RegisterObjectDefinition(name, def);
      }
      

      public void Register(Type from, Func<string, object> factoryMethod, Scope scope)
      {
         throw new NotImplementedException();
      }

      public IMetaContainer CreateChildContainer()
      {
         return new SpringMetaContainerAdapter(new GenericApplicationContext(_context));
      }
   }
}
